package org.bravof.differ.service.core.db2ja;

import org.apache.commons.lang3.ObjectUtils;
import org.bravof.differ.entity.dbbase.SchemaBody;
import org.bravof.differ.entity.dbbase.TableBody;
import org.bravof.differ.entity.dbbase.TableField;
import org.bravof.differ.entity.result.DifferencePO;
import org.bravof.differ.service.support.DBQueryService;
import org.bravof.differ.util.CommonConst;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

import static org.bravof.differ.util.CommonConst.MYSQL.*;

/**
 * DB模型转化为Java模型
 */
@Service
public class DB2ModelService {
    @Resource
    private DBQueryService dbQueryService;

    /**
     * 比对两个库的模型，生成比对结果
     *
     * @param leftId  左库ID，视为变动产生的源头
     * @param rightId 右库ID，视为变动应用的对象
     */
    public DifferencePO tellDifference(String leftId, String rightId) {
        SchemaBody left = dbQueryService.getSchemaStructById(leftId);
        SchemaBody right = dbQueryService.getSchemaStructById(rightId);
        // 以左库为基准，比对右库中所不存在的变化
        LinkedHashMap<String, TableBody> leftTableMap = left.getTableMap();
        LinkedHashMap<String, TableBody> rightTableMap = right.getTableMap();

        Set<Map.Entry<String, TableBody>> entries = leftTableMap.entrySet();
        Iterator<Map.Entry<String, TableBody>> iterator = entries.iterator();

        DifferencePO po = new DifferencePO();
        po.setChangeSql(new ArrayList<>());

        while (iterator.hasNext()) {
            Map.Entry<String, TableBody> next = iterator.next();
            TableBody leftTable = next.getValue();
            TableBody rightTable = rightTableMap.get(next.getKey());
            // 比对两表差异，生成差异补齐sql
            List<String> strings = _diffTable(leftTable, rightTable, leftId, rightId);
            po.getChangeSql().addAll(strings);
        }
        return po;
    }

    /**
     * 比对表生成sql列表
     *
     * @param left
     * @param right
     * @param leftId
     * @param rightId
     * @return
     */
    private List<String> _diffTable(TableBody left, TableBody right, String leftId, String rightId) {
        List<String> fixSqlList = new ArrayList<>();

        // 1.当右表不存在时，直接返回左表的建表语句
        if (null == right) {
            fixSqlList.add(dbQueryService.generateWholeTableSql(leftId, left.getTableName()) + CommonConst.SYMBOL_SEMICOLON);
            return fixSqlList;
        }

        // 2.当右表存在时，比较二者差异
        LinkedHashMap<String, TableField> leftFieldMap = left.getFieldMap();
        LinkedHashMap<String, TableField> rightFieldMap = right.getFieldMap();

        String lastFieldName = null;

        Set<Map.Entry<String, TableField>> entries = leftFieldMap.entrySet();
        Iterator<Map.Entry<String, TableField>> iterator = entries.iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, TableField> next = iterator.next();
            TableField leftTableField = next.getValue();
            TableField rightTableField = rightFieldMap.get(next.getKey());
            // 如果两个字段完全一致，则无需比较，跳过此字段
            if (null != rightTableField && fieldEqualOrNot(leftTableField, rightTableField)) {

            } else {
                fixSqlList.add(_diffField(leftTableField, rightTableField, left.getTableName(), lastFieldName));
            }
            lastFieldName = leftTableField.getName();
        }
        return fixSqlList;
    }

    /**
     * 比对字段，生成sql
     *
     * @param left
     * @param right
     * @param tableName
     * @param lastFieldName 上一个字段的名称
     * @return
     */
    private String _diffField(TableField left, TableField right, String tableName, String lastFieldName) {
        String sql = "";
        // 1.如果右字段不存在，返回创建左字段的sql
        if (null == right) {
            sql += DDL_ALTER_TABLE + tableName + DDL_ADD + left.getName() + DDL_BLANKET + left.getFieldTypeEnum().name().toLowerCase(Locale.ROOT);
        } else {
            // 2.如果右字段存在，则根据其差异生成sql
            sql += DDL_ALTER_TABLE + tableName + DDL_MODIFY + left.getName() + DDL_BLANKET + left.getFieldTypeEnum().name().toLowerCase(Locale.ROOT);
        }
        if (null != left.getLength() && left.getLength() > 0) {
            sql += CommonConst.SYMBOL_BRACKETS_LEFT;
            sql += left.getLength();
            if (null != left.getSecondLength() && left.getSecondLength() > 0) {
                sql += CommonConst.SYMBOL_COMMA + left.getSecondLength();
            }
            sql += CommonConst.SYMBOL_BRACKETS_RIGHT;
        }
        if (left.getAutoIncr()) {
            sql += DDL_AUTO_INCR;
        } else {
            sql += (left.getNotNull() ? DDL_NOT + DDL_NULL : DDL_NULL);
        }
        if (null != left.getComment()) {
            sql += DDL_COMMENT + CommonConst.SYMBOL_S_Q + left.getComment() + CommonConst.SYMBOL_S_Q;
        }
        if (ObjectUtils.allNotNull(lastFieldName)) {
            sql += DDL_AFTER + lastFieldName;
        } else {
            sql += DDL_FIRST;
        }
        return sql + CommonConst.SYMBOL_SEMICOLON;
    }

    /**
     * 比较两个字段是否相同
     *
     * @param l
     * @param r
     * @return
     */
    private boolean fieldEqualOrNot(TableField l, TableField r) {
        if (ObjectUtils.allNull(r)) return false;

        if (!l.getName().equals(r.getName())) return false;
        if (!l.getFieldTypeEnum().equals(r.getFieldTypeEnum())) return false;

        Integer lengthL = l.getLength();
        Integer lengthR = r.getLength();

        if ((lengthL == null && lengthR != null)
                || (lengthL != null && lengthR == null)
                || (lengthL != null && lengthR != null && !l.getLength().equals(r.getLength()))) {
            return false;
        }

        String defaultL = l.getDefaultValue();
        String defaultR = r.getDefaultValue();
        if (!Objects.equals(defaultL, defaultR)) return false;

        String commentL = l.getComment();
        String commentR = r.getComment();
        if (!Objects.equals(commentL, commentR)) return false;

        if (!Objects.equals(l.getNotNull(), r.getNotNull())) return false;
        if (!Objects.equals(l.getAutoIncr(), r.getAutoIncr())) return false;

        return true;
    }
}
